// Thud - a dynamic object-oriented language
// Copyright (C) 2009,2010 David H. Hovemeyer <david.hovemeyer@gmail.com>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

/*
 * VM_Thread.h
 *
 *  Created on: Jan 11, 2010
 *      Author: dhovemey
 */

#ifndef VM_THREAD_H_
#define VM_THREAD_H_

#include <cassert>
#include <cstdint>
#include <vector>

#include "vm/Method.h"
#include "interp/VM_Method.h"
class VM_Interpreter;
class VM_Object;

//
// VM_Frame and VM_ThreadFrame stack -
// stack frames for bytecode methods
//

struct VM_Frame {
	VM_Method *method;                       // method being executed
	uint16_t pc;                             // offset of current bytecode instruction
	VM_ThreadOperandStack::size_type localsStart; // index of local variable 0 (containing receiver object)
	const uint8_t *code;

	VM_Frame()
	{ }

	VM_Frame(VM_Method *method_, uint16_t pc_, VM_ThreadOperandStack::size_type locals_)
		: method(method_)
		, pc(pc_)
		, localsStart(locals_)
		, code(method_->bytecodeMethod->getCode())
	{ }
};

typedef std::vector<VM_Frame> VM_ThreadFrameStack;

//
// VM_ObjectGuard and VM_ObjectPointer - protect
// VM_Objects from garbage collection
//

class VM_ObjectPointer;
class VM_Thread;

// A single VM_ObjectGuard object is placed in a method
// which defines VM_ObjectPointers.
// It keeps track of all of the VM_ObjectPointers,
// and is part of a stack of VM_ObjectGuards maintained
// by each VM_Thread.  When a GC is initiated,
// the stack of VM_ObjectGuards in the thread(s)
// should have a complete list of all live VM_Objects which
// are stack-local.
class VM_ObjectGuard {
private:
	VM_Thread *m_vmThread;
	VM_ObjectGuard *m_next;
	VM_ObjectPointer *m_head;

	VM_ObjectGuard(const VM_ObjectGuard &);
	VM_ObjectGuard& operator=(const VM_ObjectGuard &);

public:
	VM_ObjectGuard(VM_Thread *vmThread);
	~VM_ObjectGuard();

	void setNext(VM_ObjectGuard *next) { m_next = next; }
	VM_ObjectGuard *getNext() { return m_next; }

	void addPointer(VM_ObjectPointer *objPtr);
};

// A VM_ObjectPointer is a live stack-local reference
// to a VM_Object.
class VM_ObjectPointer {
private:
	VM_ObjectGuard &m_guard;
	VM_Object *m_obj;
	VM_ObjectPointer *m_next;

	VM_ObjectPointer(const VM_ObjectPointer &);

public:
	VM_ObjectPointer(VM_ObjectGuard &guard);
	VM_ObjectPointer(VM_ObjectGuard &guard, VM_Object *obj);
	~VM_ObjectPointer();

	VM_ObjectPointer& operator=(const VM_ObjectPointer &rhs);
	VM_ObjectPointer& operator=(VM_Object *obj);

	VM_Object *get() const { return m_obj; }

	VM_Object *operator->() const { return m_obj; }

	void setNext(VM_ObjectPointer *next) { m_next = next; }
	VM_ObjectPointer *getNext() { return m_next; }
};

// Create a VM_ObjectGuard object
#define VM_GUARD(threadPtr)       VM_ObjectGuard guard_(threadPtr)

// Declare a VM_ObjectPointer without initializing it
#define VM_PTR(ptrName)           VM_ObjectPointer ptrName(guard_)

// Declare a VM_ObjectPointer, initializing it with given value
#define VM_PTRI(ptrName, initVal) VM_ObjectPointer ptrName(guard_, initVal)

//
// VM_Thread - a thread of control with the VM
//

class VM_Thread {
private:
	VM_Interpreter *m_interp;
	VM_ThreadOperandStack m_operandStack;
	VM_ThreadFrameStack m_frameStack;
#ifndef NDEBUG
	bool m_trace;
#endif
	VM_ObjectGuard *m_topGuard;

	VM_Thread(const VM_Thread &);
	VM_Thread& operator=(const VM_Thread &);

public:
	VM_Thread(VM_Interpreter *interp);
	~VM_Thread();

	VM_Interpreter *getInterpreter() { return m_interp; }

#ifndef NDEBUG
	void setTrace(bool trace) { m_trace = trace; }
#endif

	void pushOperand(VM_Object *value) { m_operandStack.push_back(value); }

	VM_Object *popOperand() {
		assert(!m_operandStack.empty());
		VM_Object *val = m_operandStack.back();
		m_operandStack.pop_back();
		return val;
	}

	VM_Object *topOperand() {
		assert(!m_operandStack.empty());
		return m_operandStack.back();
	}

	size_t getStackDepth() { return size_t(m_operandStack.size()); }

	void interpretMethod(VM_Method *method, uint8_t numArgs);

	void pushObjectGuard(VM_ObjectGuard *objGuard);
	void popObjectGuard(VM_ObjectGuard *objGuardExpectedAtTop);

	void markReachableObjects();

private:
	void enterFrame(VM_Method *method, uint8_t numArgs, VM_Frame &frame);
	void leaveFrame(VM_ThreadFrameStack::size_type frameStackBase, VM_Frame &frame);

#ifndef NDEBUG
	void dumpStack();
#endif
};

#endif /* VM_THREAD_H_ */
